<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <style>
        body { margin: 0; padding: 0; background: #000; overflow: hidden; }
        canvas { touch-action: none; }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

// 屏幕适配
function resizeCanvas() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);

const particles = [];
const fireworks = [];

class Particle {
    constructor(x, y, isFirework) {
        this.x = x;
        this.y = y;
        this.isFirework = isFirework;
        
        if (!isFirework) {
            // 升空粒子参数
            this.life = 3;
            this.startY = canvas.height;
            this.targetY = canvas.height / 3;
            this.duration = 3;
            this.size = 2;
            this.color = '#fff';
            this.speed = (this.startY - this.targetY) / this.duration;
        } else {
            // 爆炸粒子参数
            this.life = Math.random() * 2 + 2; // 2-4秒寿命
            this.angle = Math.random() * Math.PI * 2;
            this.speed = Math.random() * 8 + 4;
            this.size = Math.random() * 4 + 2;
            this.color = this.generateSparkColor();
            this.vx = Math.cos(this.angle) * this.speed;
            this.vy = Math.sin(this.angle) * this.speed;
            this.gravity = 0.2;
        }
        this.age = 0;
    }

    generateSparkColor() {
        // 生成明亮颜色（80%纯色+20%渐变）
        if(Math.random() < 0.8) {
            return `hsl(${Math.random() * 360}, 100%, 70%)`;
        }
        const grad = ctx.createRadialGradient(0, 0, 0, 0, 0, this.size);
        grad.addColorStop(0, `hsla(${Math.random() * 360}, 100%, 70%, 1)`);
        grad.addColorStop(1, `hsla(${Math.random() * 360}, 100%, 50%, 0)`);
        return grad;
    }

    update(deltaTime) {
        this.age += deltaTime;
        
        if (!this.isFirework) {
            // 匀速上升带尾迹
            this.y -= this.speed * deltaTime;
            return this.age < this.life;
        } else {
            // 爆炸粒子物理模拟
            this.vy += this.gravity;
            this.x += this.vx;
            this.y += this.vy;
            return this.age < this.life;
        }
    }

    explode() {
        for(let i=0; i<100; i++) {
            fireworks.push(new Particle(this.x, this.y, true));
        }
        return false;
    }

    draw() {
        ctx.beginPath();
        
        // 绘制光效
        if(this.isFirework) {
            ctx.shadowColor = this.color;
            ctx.shadowBlur = this.size * 4;
            ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
        } else {
            ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
        }
        
        ctx.fillStyle = this.color;
        ctx.fill();
        
        // 重置阴影
        ctx.shadowColor = 'transparent';
    }
}

// 触摸事件处理
canvas.addEventListener('touchstart', (e) => {
    e.preventDefault();
    const touch = e.touches[0];
    particles.push(new Particle(touch.clientX, canvas.height, false));
});

// 动画循环
let lastTime = 0;
function animate(timestamp) {
    const deltaTime = (timestamp - lastTime) / 1000;
    lastTime = timestamp;

    // 尾迹效果控制
    ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 双缓冲更新
    [particles, fireworks].forEach(arr => {
        for(let i = arr.length-1; i>=0; i--) {
            const shouldKeep = arr[i].update(deltaTime);
            if(!shouldKeep) {
                if(!arr[i].isFirework) arr[i].explode();
                arr.splice(i, 1);
            } else {
                arr[i].draw();
            }
        }
    });

    requestAnimationFrame(animate);
}

animate(0);
</script>
</body>
</html>